package main

import "fmt"

func main() {
	var sum int
	for i := 0; i < 10; i++ {
		sum += i
	}
	println("loop statement", sum)

	sum = 0
	// Go 语言支持多变量的循环语句
	for i, j, k := 0, 1, 2; i < 20 && j < 10 && k < 30; i, j, k = i+1, j+1, k+4 {
		sum += (i + j + k)

	}

	println("multiply variables loop statement", sum)

	// 除了循环体部分之外, 其他部分是可选的, 如
	for {
		break
	}

	var sl = []int{1, 2, 3, 4, 5}
	for i := 0; i < len(sl); i++ {
		fmt.Printf("sl[%d] = %d\n", i, sl[i])
	}

	// 对于遍历切片中的元素，Go语言提供了一个更方便的“语法糖”形式: for range
	for i, v := range sl {
		fmt.Printf("sl[%d] = %d\n", i, v)
	}

	// for range 针对复杂类型操作
	// 1. 字符串
	var s = "中国人"
	for i, v := range s {
		// 对于string 类型来说, 每次循环得到的v值是一个Unicode字符码点, 也就是rune类型值, 而不是一个字节
		// 返回的第一个值i为Unicode字符码点的内存编码(UTF-8)的第一个字节在字符串内存序列中的位置
		fmt.Printf("%d %s 0x%x\n", i, string(v), v)
	}

	// 2. map
	// 要对map进行循环操作, for range 是唯一的办法
	var m = map[string]int{
		"Rob":  67,
		"Russ": 39,
		"John": 29,
	}
	for k, v := range m {
		fmt.Println(k, v)
	}

	// 3. channel
	// channel 是Go语言提供的并发设计的原语, 它用于多个Goroutinue之间的通信
	// 当 channel 类型变量作为 for range 语句的迭代对象时, for range 会尝试从channel 中读取数据
	// var c = make(chan int)
	// for v := range c {

	// }

	// 带label的continue语句, 通常出现于嵌套循环语句中, 被用于跳转到外层循环并继续执行外层循环语句的下一个迭代
	var sll = [][]int{
		{1, 34, 26, 35, 78},
		{3, 45, 13, 24, 99},
		{101, 13, 38, 7, 127},
		{54, 27, 40, 83, 81},
	}

outerloop:
	for i := 0; i < len(sll); i++ {
		for j := 0; j < len(sll[i]); j++ {
			if sll[i][j] == 13 {
				fmt.Printf("found 13 at [%d, %d]\n", i, j)
				continue outerloop // 一旦使用goto跳转, 那么不管时内层循环还是外层循环都被毁终结, 代码将会从outerloop这个label处, 开始重新执行嵌套循环语句, 这与带label的continue的跳转语义时完全不同的。
			}
		}
	}

outerloop1:
	for i := 0; i < len(sll); i++ {
		for j := 0; j < len(sll[i]); j++ {
			if sll[i][j] == 13 {
				fmt.Printf("found 13 at [%d, %d]\n", i, j)
				break outerloop1 // 跳出outerloop1指向的整个循环
			}
		}
	}
}
